﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;

namespace Tizer.Utilities
{
    public sealed class CookieHelper
    {
        //Rijndael Key size is 256 bit or 32 byte, Can also be mentioned in web.config instead of hardcoding
        private static readonly byte[] Key = new byte[] { 45, 236, 171, 7, 85, 6, 41, 34, 216, 14, 78, 156, 78, 3, 103, 154, 9, 150, 65, 54, 226, 95, 68, 79, 159, 36, 246, 57, 177, 107, 116, 8 };

        [DebuggerStepThrough()]
        public static void Set(HttpResponse response,
        string key,
        string value,
        DateTime expire)
        {
            HttpCookie cookie = new HttpCookie(HttpUtility.UrlEncode(Encrypt(key)), HttpUtility.UrlEncode(Encrypt(value)));

            if ((expire != DateTime.MinValue) && (expire != DateTime.MaxValue))
            {
                cookie.Expires = expire;
            }

            response.Cookies.Set(cookie);
        }

        [DebuggerStepThrough()]
        public static void Set(HttpResponse response,
        string key,
        string value)
        {
            Set(response, key, value, DateTime.MaxValue);
        }

        [DebuggerStepThrough()]
        public static string Get(HttpRequest request, string key)
        {
            HttpCookie cookie = request.Cookies[HttpUtility.UrlEncode(Encrypt(key))];

            if (cookie == null)
            {
                return null;
            }

            if ((cookie.Value == null) || (cookie.Value.Length == 0))
            {
                return null;
            }

            string value = HttpUtility.UrlDecode(cookie.Value);

            return Decrypt(value);
        }

        [DebuggerStepThrough()]
        private static string Encrypt(string plain)
        {
            if ((plain == null) || (plain.Length == 0))
            {
                return null;
            }

            using (SymmetricAlgorithm crypto = CreateCrypto())
            {
                return System.Convert.ToBase64String(Read(crypto.CreateEncryptor(), Encoding.ASCII.GetBytes(plain)));
            }
        }

        [DebuggerStepThrough()]
        private static string Decrypt(string cipher)
        {
            if ((cipher == null) || (cipher.Length == 0))
            {
                return null;
            }

            using (SymmetricAlgorithm crypto = CreateCrypto())
            {
                return Encoding.ASCII.GetString(Read(crypto.CreateDecryptor(), System.Convert.FromBase64String(cipher)));
            }
        }

        [DebuggerStepThrough()]
        private static SymmetricAlgorithm CreateCrypto()
        {
            //Using Rijndael as it is much more secure among the others
            SymmetricAlgorithm crypto = new RijndaelManaged();

            crypto.Key = Key;
            crypto.IV = new byte[crypto.IV.Length];

            return crypto;
        }

        [DebuggerStepThrough()]
        private static byte[] Read(ICryptoTransform transformer,
        byte[] data)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, transformer, CryptoStreamMode.Write))
                {
                    cs.Write(data, 0, data.Length);
                    cs.FlushFinalBlock();

                    return ms.ToArray();
                }
            }
        }
    }
}
